﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Setup_Pst_Backup_2013
{
    public partial class frmSetup : Form
    {
        public enum Actions
        {
            Install,
            Uninstall,
            Nothing
        }

        public frmSetup(Actions action, bool isSilent)
        {
            InitializeComponent();

            this.DeploymentAction = action;
            this.IsSilent = isSilent;

            this.chkBxCopyingFiles.Checked = (action == Actions.Uninstall);
            this.chkBxCreatingWindowsEventLog.Checked = (action == Actions.Uninstall);
            this.chkBxRegisteringAddIn.Checked = (action == Actions.Uninstall);
            this.chkBxRegisteringSoftware.Checked = (action == Actions.Uninstall);
            switch (action)
            {
                case Actions.Install:
                    this.lblReadytoPerformeAction.Text = "Ready to install the software.";
                    break;
                case Actions.Uninstall:
                    this.lblReadytoPerformeAction.Text = "Ready to uninstall the software.";
                    break;
                case Actions.Nothing:
                    this.lblReadytoPerformeAction.Text = "No action will be performe.";
                    break;
                default:
                    this.lblReadytoPerformeAction.Text = "No action will be performe.";
                    break;
            }
        }

        #region Private Properties

        private Actions DeploymentAction { get; set; }

        private bool IsSilent { get; set; }

        #endregion Private Properties

        #region Internal Methods

        internal void PerformeDeployment()
        {
            if (this.IsSilent)
                LaunchDeployment();
            else
                this.ShowDialog();
        }

        #endregion Internal Methods

        #region Private Methods

        private void LaunchDeployment()
        {
            switch (this.DeploymentAction)
            {
                case Actions.Install:
                    this.Install();
                    break;
                case Actions.Uninstall:
                    this.Uninstall();
                    break;
                default:
                    this.Close();
                    break;
            }
        }

        private void Install()
        {
            this.CreateWindowsEventLogSource();
            this.RegisterOutlookAddIn();
            this.CopyFiles();
            this.CreateUninstallRegKeys();
            this.LogEvent("Pst Backup 2013 is now installed.", System.Diagnostics.EventLogEntryType.Information, 2);
            this.lblReadytoPerformeAction.Text = "All actions has been performed.";
        }

        private void Uninstall()
        {
            this.chkBxCreatingWindowsEventLog.Checked = false;
            if (!this.IsSilent)
                while (this.IsOutlookOpen())
                {
                    MessageBox.Show("Please, Close Outlook and press <Ok>");
                }

            this.UnregisterOutlookAddIn();
            this.DeleteFiles();
            this.DeleteUninstallRegKey();
            this.LogEvent("Pst Backup 2013 has been uninstalled.", System.Diagnostics.EventLogEntryType.Information, 3);
            this.lblReadytoPerformeAction.Text = "All actions has been performed.";
        }

        private bool IsOutlookOpen()
        {
            try
            {
                System.Diagnostics.Process[] runningProcesses = System.Diagnostics.Process.GetProcesses();
                foreach (System.Diagnostics.Process process in runningProcesses)
                {
                    if (process.ProcessName.ToLower() == "outlook")
                        return true;
                }
            }
            catch (Exception) { }
            return false;
        }

        private void CreateWindowsEventLogSource()
        {
            try
            {
                if (!System.Diagnostics.EventLog.SourceExists("Pst Backup 2013"))
                    System.Diagnostics.EventLog.CreateEventSource("Pst Backup 2013", "Application");
                this.LogEvent("Installing Pst Backup 2013", System.Diagnostics.EventLogEntryType.Information, 1);
                this.chkBxCreatingWindowsEventLog.Checked = true;
                this.Refresh();
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to create Windows Event Log Source : \r\n" + ex.Message);
            }
        }

        private void RegisterOutlookAddIn()
        {
            try
            {
                Microsoft.Win32.RegistryKey hklm = Microsoft.Win32.Registry.LocalMachine;
                Microsoft.Win32.RegistryKey addInsKey = this.GetOutlookAddinsRegKey();
                if (addInsKey != null)
                {
                    Microsoft.Win32.RegistryKey thisAddinKey = addInsKey.CreateSubKey("EasyCompany.Pst Backup 2013");
                    thisAddinKey.SetValue("Description", "Pst Backup 2013", Microsoft.Win32.RegistryValueKind.String);
                    thisAddinKey.SetValue("FriendlyName", "Pst Backup 2013", Microsoft.Win32.RegistryValueKind.String);
                    thisAddinKey.SetValue("LoadBehavior", 3, Microsoft.Win32.RegistryValueKind.DWord);
                    thisAddinKey.SetValue("Manifest", this.GetDeploymentPath() + @"Pst Backup 2013.vsto|vstolocal", Microsoft.Win32.RegistryValueKind.String);
                    addInsKey.Close();
                    this.LogEvent("Pst Backup 2013 Outlook Add-In is now register.", System.Diagnostics.EventLogEntryType.Information, 4);
                    this.chkBxRegisteringAddIn.Checked = true;
                }
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to register Outlook Add-In : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to register Outlook Add-In : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20005);
            }
        }

        private Microsoft.Win32.RegistryKey GetOutlookAddinsRegKey()
        {
            Microsoft.Win32.RegistryKey addInsRegKey = null;

            try
            {
                Microsoft.Win32.RegistryKey baseKey64 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office\15.0\Outlook\InstallRoot", false);
                Microsoft.Win32.RegistryKey baseKey32 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Office\15.0\Outlook\InstallRoot", false);

                if (baseKey32 != null)
                {
                    addInsRegKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Office\Outlook\Addins", true);
                }
                else
                    if (baseKey64 != null)
                    {
                        addInsRegKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office\Outlook\Addins", true);
                    }

                if (addInsRegKey == null)
                    throw new Exception(@"Can't find HKEY_LOCAL_MACHINE\SOFTWARE\???\Microsoft\Office\Outlook\Addins");
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to find 'Addins' registry key : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to find 'Addins' registry key : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20005);
            }

            return addInsRegKey;
        }

        private void CopyFiles()
        {
            try
            {
                System.Reflection.Assembly _assembly;
                byte[] buffer = new byte[1024];
                string _destinationPath = this.GetDeploymentPath();

                _assembly = System.Reflection.Assembly.GetExecutingAssembly();
                //string[] names = _assembly.GetManifestResourceNames();
                //foreach (string name in names)
                //{
                //    Console.Write(name);
                //}

                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Pst-Backup-Agent.exe"), "Pst-Backup-Agent.exe", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Pst Backup 2013.vsto"), "Pst Backup 2013.vsto", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Pst Backup 2013.dll.manifest"), "Pst Backup 2013.dll.manifest", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Pst Backup 2013.dll"), "Pst Backup 2013.dll", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Microsoft.Office.Tools.Outlook.v4.0.Utilities.dll"), "Microsoft.Office.Tools.Outlook.v4.0.Utilities.dll", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.Microsoft.Office.Tools.Common.v4.0.Utilities.dll"), "Microsoft.Office.Tools.Common.v4.0.Utilities.dll", _destinationPath);
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.fr-Pst-Backup-Agent.resources.dll"), "Pst-Backup-Agent.resources.dll", _destinationPath + @"fr\");
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.fr-Pst Backup 2013.resources.dll"), "Pst Backup 2013.resources.dll", _destinationPath + @"fr\");
                this.CopyFile(_assembly.GetManifestResourceStream("Setup_Pst_Backup_2013.Resources.en-Pst-Backup-Agent.resources.dll"), "Pst-Backup-Agent.resources.dll", _destinationPath + @"en\");

                try
                {
                    string ownPath = Application.ExecutablePath;
                    System.IO.FileInfo itSelf = new System.IO.FileInfo(ownPath);
                    itSelf.CopyTo(@"C:\Windows\Installer\" + itSelf.Name, true);
                }
                catch (Exception) { }

                this.LogEvent("Pst Backup 2013 files has been copied.", System.Diagnostics.EventLogEntryType.Information, 5);
                this.chkBxCopyingFiles.Checked = true;
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to copy files : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to copy files : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20007);
            }
        }

        private void CopyFile(System.IO.Stream sourceFile, string filename, string destinationPath)
        {
            try
            {
                if (!System.IO.Directory.Exists(destinationPath))
                    System.IO.Directory.CreateDirectory(destinationPath);

                System.IO.StreamWriter writer = new System.IO.StreamWriter(destinationPath + filename, false);
                sourceFile.CopyTo(writer.BaseStream);
                sourceFile.Flush();
                sourceFile.Close();
                writer.Close();
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to copy file (" + filename + ") : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to copy file (" + filename + ") : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20007);
            }
        }

        private void CreateUninstallRegKeys()
        {
            try
            {
                string _destinationPath = this.GetDeploymentPath();
                string ownPath = Application.ExecutablePath;
                System.IO.FileInfo itSelf = new System.IO.FileInfo(ownPath);
                string setupFullPath = @"C:\Windows\Installer\" + itSelf.Name;
                Microsoft.Win32.RegistryKey baseKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", true);
                baseKey = baseKey.CreateSubKey("Pst Backup 2013");

                this.CreateStringKey(baseKey, "DisplayName", "Pst Backup 2013");
                this.CreateStringKey(baseKey, "UninstallString", setupFullPath + " /Action=Uninstall");
                this.CreateStringKey(baseKey, "QuietUninstallString", setupFullPath + " /Action=Uninstall /s");
                this.CreateStringKey(baseKey, "InstallLocation", _destinationPath);
                this.CreateStringKey(baseKey, "DisplayIcon", setupFullPath);
                this.CreateStringKey(baseKey, "Publisher", "EasyCompany");
                this.CreateStringKey(baseKey, "HelpLink", "https://pstbackup2013.codeplex.com/documentation");
                this.CreateStringKey(baseKey, "URLUpdateInfo", "https://pstbackup2013.codeplex.com/releases");
                this.CreateStringKey(baseKey, "URLInfoAbout", "https://pstbackup2013.codeplex.com/");
                this.CreateStringKey(baseKey, "DisplayVersion", Application.ProductVersion.ToString());
                Version productVersion = new Version(Application.ProductVersion);
                this.CreateIntKey(baseKey, "VersionMajor", productVersion.Major);
                this.CreateIntKey(baseKey, "VersionMinor", productVersion.Minor);
                this.CreateIntKey(baseKey, "NoModify", 1);
                this.CreateIntKey(baseKey, "NoRepair", 1);
                int folderSize = (int)(GetDirSize(_destinationPath) / 1024);
                this.CreateIntKey(baseKey, "EstimatedSize", folderSize);

                baseKey.Close();

                this.LogEvent("Pst Backup 2013 is register on this computer.", System.Diagnostics.EventLogEntryType.Information, 6);
                this.chkBxRegisteringSoftware.Checked = true;
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to create uninstallKey in registry : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to create uninstallKey in registry : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20010);
            }
        }

        private void CreateStringKey(Microsoft.Win32.RegistryKey baseKey, string keyName, string value)
        {
            try
            {
                baseKey.SetValue(keyName, value, Microsoft.Win32.RegistryValueKind.String);
            }
            catch (Exception) { }
        }

        private void CreateIntKey(Microsoft.Win32.RegistryKey baseKey, string keyName, int value)
        {
            try
            {
                baseKey.SetValue(keyName, value, Microsoft.Win32.RegistryValueKind.DWord);
            }
            catch (Exception) { }
        }

        private long GetDirSize(string directory)
        {
            try
            {
                System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(directory);
                if (dirInfo.Exists)
                    return dirInfo.GetFiles().Sum(fi => fi.Length) +
                           dirInfo.GetDirectories().Sum(di => GetDirSize(di.FullName));
            }
            catch (Exception) { }
            return 0;
        }

        private void UnregisterOutlookAddIn()
        {
            try
            {
                Microsoft.Win32.RegistryKey hklm = Microsoft.Win32.Registry.LocalMachine;
                Microsoft.Win32.RegistryKey addInsKey = this.GetOutlookAddinsRegKey();

                if (addInsKey != null)
                {
                    addInsKey.DeleteSubKey("EasyCompany.Pst Backup 2013", false);
                    addInsKey.Close();

                    this.LogEvent("Pst Backup 2013 Outlook Add-In is now unregister.", System.Diagnostics.EventLogEntryType.Information, 7);
                    this.chkBxRegisteringAddIn.Checked = false;
                }
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Unable to unregister Outlook Add-In : \r\n" + ex.Message);
                else
                    this.LogEvent("Unable to unregister Outlook Add-In : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20008);
            }
        }

        private void DeleteFiles()
        {
            string _destinationPath = this.GetDeploymentPath();

            System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(_destinationPath);
            if (dirInfo.Exists)
            {
                foreach (System.IO.FileInfo file in dirInfo.GetFiles())
                {
                    try { file.Delete(); }
                    catch (Exception) { }
                }
                foreach (System.IO.DirectoryInfo folder in dirInfo.GetDirectories())
                {
                    try { folder.Delete(true); }
                    catch (Exception) { }
                }
                if (dirInfo.GetFiles().Length == 0)
                    try
                    {
                        dirInfo.Delete(true);
                    }
                    catch (Exception) { }
                if (dirInfo.Parent.GetFiles().Length == 0 && dirInfo.Parent.GetDirectories().Length == 0)
                    try
                    {
                        dirInfo.Parent.Delete(true);
                    }
                    catch (Exception) { }
            }

            this.LogEvent("Pst Backup 2013 files has been deleted.", System.Diagnostics.EventLogEntryType.Information, 8);
            this.chkBxCopyingFiles.Checked = false;
        }

        private void DeleteUninstallRegKey()
        {
            try
            {
                Microsoft.Win32.RegistryKey baseKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", true);
                baseKey.DeleteSubKey("Pst Backup 2013", false);
                baseKey.Close();

                this.LogEvent("Pst Backup 2013 has been unregister on this computer.", System.Diagnostics.EventLogEntryType.Information, 9);
                this.chkBxRegisteringSoftware.Checked = false;
            }
            catch (Exception) { }
        }

        private void LogEvent(string message, System.Diagnostics.EventLogEntryType type, int id)
        {
            try
            {
                System.Diagnostics.EventLog.WriteEntry("Pst Backup 2013", message, type, id);
            }
            catch (Exception) { }
        }

        private string GetDeploymentPath()
        {
            string path = @"C:\Program Files\EasyCompany\Pst Backup 2013\";

            try
            {
                path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + @"\EasyCompany\Pst Backup 2013\";
            }
            catch (Exception ex)
            {
                if (!this.IsSilent)
                    MessageBox.Show("Can't find %programfiles% : \r\n" + ex.Message);
                else
                    this.LogEvent("Can't find %programfiles% : \r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error, 20006);
            }
            return path;
        }

        #endregion Private Methods

        #region UI Events

        private void btnStart_Click(object sender, EventArgs e)
        {
            this.btnStart.Enabled = false;
            LaunchDeployment();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
        }

        #endregion UI Events
    }
}
